
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Xml;


using Swaf.Container;
using Swaf.BizObj.fieldValidator;
using Swaf.Logging;
using Swaf.Script;

namespace Swaf.BizObj
{
	/// <summary>
	/// This class provides the core implementation of the IBizObj interface and also
	/// implements the ICustomTypeDescriptor interface in order to allow a bizobj to be
	/// bound to with .NET data binding.  This class provides a set of abstract methods
	/// that any child must implement in order to inherit from this class.
	/// 
	/// This is the class that provides support for decorators, deep name addresses, and 
	/// other bizobj technologies.  Child classes are expected to provide the storage for 
	/// values being put into the bizobj and to maintain the various system values such as
	/// dirty, validated, isNew and isDeleted because these things require storage to manage
	/// their state.  This class is intended to be usable by different implementations that
	/// store thier values different then just in memory like FullBizObj.
	/// </summary>
	[Serializable]
	public abstract class BaseBizObj : IBizObj, ICustomTypeDescriptor
	{
		public static string DirtyFlagName = "_dirty";
		public static string ValidatedFlagName = "_validated";
		public static string IsNewFlagName = "_new";
		public static string IsDeletedFlagName = "_delete";

		private static IClassFactory s_bizObjFieldFactory = null;
		protected IBizObjDefinition m_bizObjType;

		public static string DefaultFieldDecoInfo { get { return "ExternalUntrusted"; } }

		/// <summary>
		/// creates a new empty bizobj from the given definition.  The bizobj definition
		/// can be null, but if so the bizobj cannot be used until the setType is called.
		/// The intention of that is to allow bizobjs to be created before their type is fully
		/// known and then changed during startup.
		/// </summary>
		/// <param name="bizObjType">The bizobj type to be used by this instance.</param>
		public BaseBizObj(IBizObjDefinition bizObjType)
		{
			m_bizObjType = bizObjType;
		}

		public void setType(IBizObjDefinition bizObjType)
		{
			m_bizObjType = bizObjType;
		}

		public abstract bool containsSingleField(string fieldName);
		public abstract bool isNew { get;set;}
		public abstract bool isDeleted { get;set;}
		public abstract object Clone();
		public abstract IEnumerator GetEnumerator();

		protected abstract void removeValue(string fieldName);
		protected abstract BitFlags DirtyFlags{get;}
		protected abstract BitFlags ValidatedFlags { get;}
        protected abstract bool isSingleFieldSet(string singleName);
		protected abstract object getSingleName(string singleName);
		protected abstract object getSingleName(string singleName, object[] extra);
		protected abstract void putSingleName(string singleName, object val, object extraInfo, bool shouldValueBeFieldType);
		protected abstract object createPlatformTypeInstance(BizObjField field, object val);

		/// <summary>
		/// The factory for bizobj fields.  This is a shortcut for getting access to the factory and
		/// the actual instance is pulled from the application factories on first use.
		/// </summary>
		public static IClassFactory BizObjFieldFactory
		{
			get
			{
				if (s_bizObjFieldFactory == null)
					s_bizObjFieldFactory = Application.currentApp.factories["BizObjBizFieldTypes"];
				return s_bizObjFieldFactory;
			}
		}

		public static void resetFactoryReferences()
		{
			s_bizObjFieldFactory = null;
		}

		/// <summary>
		/// Provides the XML serialization of this instance rather then the default class name.
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return Xml;
		}
		#region IBizObj Members

		/// <see>IBizObj.type</see>
		public IBizObjDefinition type
		{
			get { return m_bizObjType; }
		}

		///<see>IBizObj.isKindOf</see>
		public bool isKindOf(string typeName)
		{
			return m_bizObjType.isKindOf(typeName);
		}

		///<see>IBizObj.isFieldSet</see>
		public bool isFieldSet(string fieldName)
		{
			bool isSet = true;
			if (NameAddress.isSingleAddress(fieldName))
                isSet = isSingleFieldSet(fieldName);
			else
			{
				object target = this.get(NameAddress.getAllButLast(fieldName));
				if (target is IBizObj)
					isSet = ((IBizObj)target).isFieldSet(NameAddress.getLastAddress(fieldName));
			}
			return isSet;
		}

		///<see>IBizObj.unsetField</see>
		public void unsetField(string fieldName)
		{
			if (NameAddress.isSingleAddress(fieldName))
				removeValue(fieldName);
			else
			{
				object target = this.get(NameAddress.getAllButLast(fieldName));
				if (target is IBizObj)
					((IBizObj)target).unsetField(NameAddress.getLastAddress(fieldName));
			}
		}

		/// <summary>
		/// Will get a value from the bizobj.  Supports deep names and is the main source of
		/// processing field decorators when getting a decorated value.  If a deep name is
		/// used on a field that is a bizobj type that is also decorated, the bizobj will be 
		/// processed through the decorators before continuing on with deep name support.  This
		/// works wells when a field of type bizobj is also decorated with something that
		/// changes the type completely.  For example, the encryption decorator will change
		/// the actual type from bizobj to string.  In order to properly support decorating
		/// a bizobj with a decorator like that, this method needs to "un-encrypt" the bizobj.
		/// 
		/// This method completely handles deep name support for its children.  If a deep
		/// name is used, it will get the first name value from the child and then continue 
		/// with the deep name processing.
		/// </summary>
		/// <seealso cref="IBizObj.get"/>
		/// <param name="name"></param>
		/// <param name="p"></param>
		/// <returns></returns>
		public object get(string name, params object[] p)
		{
			object val = null;
			string firstName = NameAddress.getFirstAddress(name);
			if (!Cmn.IsEmpty(firstName))
			{
				if (string.Compare(firstName, "_keyValue", false) == 0)
					return KeyValue;

				BizObjField field = m_bizObjType.Fields[firstName] as BizObjField;
				if (field != null)
				{
					bool isSingleName = NameAddress.isSingleAddress(name);
					if (isSingleName && field.BizType != BizFieldType.Method)
						val = getSingleName(firstName, p);
					else
						val = getSingleName(firstName);

					if (field.HasStandInDecorators)
					{
						bool shouldValueBeFieldType = true;
						foreach (IDecorator deco in field.Decorators.Values)
							if (deco.IsStandin && deco is IBizObjDecorator)
								((IBizObjDecorator)deco).get(this, field, ref val, p, ref shouldValueBeFieldType);

						if (shouldValueBeFieldType)
							val = createPlatformTypeInstance(field, val);

					}

					if (val is INameAddressSupport)
						val = ((INameAddressSupport)val).get(NameAddress.getRemainingAddress(name));
				}
				else
					throw new BadBizObjFieldException(m_bizObjType.Name, firstName, "Bad field name");
			}
			else
				val = this;

			return val;
		}

		/// <summary>
		/// A helper method that passes control to the main get.
		/// </summary>
		/// <seealso cref="IBizObj.getItem"/>
		/// <seealso cref="get"/>
		/// <param name="name"></param>
		/// <param name="p"></param>
		/// <returns></returns>
		public object getItem(string name, params object[] p)
		{
			return get(name, p);
		}

		/// <summary>
		/// Will put a value into the bizobj.  Supports deep name addressing and decorators
		/// during the put operation.  This method
		/// 
		/// This method completely handles deep name support for its children.  If a deep
		/// name is used, it will get the first name value from the child and then continue 
		/// with the deep name processing.  If the first name is also decoratorated, it will 
		/// also put back the main value so that the changes made deeper within it will be
		/// persisted.  For example, if the field is a bizobj type, decorated with the encryption
		/// decorator, and one of its fields are being changed, the child bizobj must be put
		/// back into this bizobj through its decorators in order to re-encrypt the bizobj and have
		/// the changes stay.  Without this, the encryption decorator (or other decorators that
		/// change the base type from bizobj to something else) would not be able to be applied
		/// to a bizobj or bizobjlist field type.
		/// </summary>
		/// <param name="name">the name of the field to change.  Supports deep names</param>
		/// <param name="val">the value to put in the field.</param>
		public void put(string name, object val)
		{
			string firstName = NameAddress.getFirstAddress(name);
			if (firstName != null)
			{
				BizObjField field = m_bizObjType.Fields[firstName] as BizObjField;
				if (field != null)
				{
					if (NameAddress.isSingleAddress(name))
					{
						bool shouldValueBeFieldType = true;
						if (field.HasStandInDecorators)
							foreach (IDecorator deco in field.Decorators.Values)
								if (deco.IsStandin && deco is IBizObjDecorator)
									((IBizObjDecorator)deco).put(this, field, ref val, null, ref shouldValueBeFieldType);
						putSingleName(firstName, val,null, shouldValueBeFieldType);
						DirtyFlags.setBit(field.BizObjIndex, true);
						ValidatedFlags.setBit(field.BizObjIndex, false);
					}
					else
					{
						//By calling get here, any decorators on our field will be called before 
						//continuing on with the deep name get for the final value.
						INameAddressSupport fieldVal = get(firstName+".") as INameAddressSupport;
						if (fieldVal != null)
						{
							string remaining = NameAddress.getRemainingAddress(name);
							fieldVal.put(remaining, val);
							if(!(fieldVal is IBizObj || fieldVal is IBizObjList))
								DirtyFlags.setBit(field.BizObjIndex, true);
						}

						//If the field has standin decorators we actually have to put the fieldVal back into itself
						//in order to properly persist values changes deep within the bizobj.  This is critical
						//for proper handling when decorators change the type of the bizobj/bizobj list to something
						//else like a string or byte array.
						if (field.HasStandInDecorators)
						{
							val = fieldVal;
							bool shouldValueBeFieldType = true;
							foreach (IDecorator deco in field.Decorators.Values)
								if (deco.IsStandin && deco is IBizObjDecorator)
									((IBizObjDecorator)deco).put(this, field, ref val, null, ref shouldValueBeFieldType);
							putSingleName(firstName, val, null, shouldValueBeFieldType);
						}
					}
				}
				else
					throw new BadBizObjFieldException(m_bizObjType.Name, firstName, "Bad field name");
			}
		}

		/// <summary>
		/// Validates the value given against the validators specified for the field
		/// and if a valid value, does a standard put with the validated value.
		/// </summary>
		/// <seealso cref="BaseBizObj.get"/>
		/// <param name="name">The field name to put the value into. Supports deep names</param>
		/// <param name="newValue">The value to validate and put in the bizobj</param>
		public void putValidated(string name, object newValue)
		{
			if (NameAddress.isSingleAddress(name))
			{
				BizObjField field = m_bizObjType.Fields[name] as BizObjField;
				if (field != null)
				{
					object[] msgAndValue = validateAndConvert(field, newValue);
					if (msgAndValue[0] != null)
						throw new BadBizObjFieldException(this, name, (string)msgAndValue[0]);
					put(name, msgAndValue[1]);
					DirtyFlags.setBit(field.BizObjIndex, true);
					ValidatedFlags.setBit(field.BizObjIndex, true);
				}
			}
			else
			{
				try
				{
					string allButLast = NameAddress.getAllButLast(name);
					object obj = get(allButLast + ".");
					if (obj is IBizObj)
						((IBizObj)obj).putValidated(NameAddress.getLastAddress(name), newValue);
					else
					{
						//If the final element is actually not an IBizObj but is an INameAddressSupport
						//We try putValidated again with all but last. This helps with special INameAddressSupport
						//Elements that act like normal fields like DecimalFormattingNode.
						if (obj is INameAddressSupport)
							putValidated(allButLast, newValue);
						else
							throw new BadBizObjFieldException(m_bizObjType.Name, name, "Invalid name address to use. Not all elements support deep names as they should");
					}
				}
				catch (BadBizObjFieldException)
				{
					throw;
				}
				catch (Exception e)
				{
					Application.globalApp.Log.write("Bad deep name, '{1}', used on bizobj type '{0}'. Exception: {2}", m_bizObjType.Name, name, e.Message);
					throw new BadBizObjFieldException(m_bizObjType.Name, name, "Invalid name address to use. Not all elements support deep names as they should");
				}
			}
		}

		/// <summary>
		/// Iterates through all of the fields in this bizobj and if it is marked as not being validated
		/// will validate it.  Any invalid fields will be added to a list and returned.
		/// </summary>
		/// <seealso cref="IBizObj.validate"/>
		/// <returns>The list of invalid fields and messages</returns>
		public List<ValidationError> validate()
		{
			List<ValidationError> errors = null;
			object[] msgAndValue = null;

			foreach (BizObjField field in m_bizObjType.Fields.Values)
			{
				if (!ValidatedFlags.getBit(field.BizObjIndex))
				{
					object origVal = get(field.Name);
					if (origVal is IBizObj)
					{
						errors = combine(errors, ((IBizObj)origVal).validate());
					}
					else if (origVal is IBizObjList)
					{
						foreach (IBizObj o in (IBizObjList)origVal)
							errors = combine(errors, o.validate());
					}
					else
					{
						msgAndValue = validateAndConvert(field, get(field.Name));
						if (msgAndValue[0] != null)
						{
							if (errors == null)
								errors = new List<ValidationError>();
							errors.Add(new ValidationError(this.type.Name, field.Name, msgAndValue[0].ToString(), msgAndValue[1]));
						}
						else
						{
							bool shouldPut =
								((msgAndValue[1] == null && origVal != null) ||
								(msgAndValue[1] != null && origVal != null && msgAndValue[1].GetType() != origVal.GetType()) ||
								(msgAndValue[1] != null && origVal != null && msgAndValue[1] is IComparable &&
									((IComparable)msgAndValue[1]).CompareTo(origVal) != 0) ||
								(!(msgAndValue[1] is IComparable) && msgAndValue[1] != origVal));
							if (shouldPut)
								put(field.Name, msgAndValue[1]);
							ValidatedFlags.setBit(field.BizObjIndex, true);
						}
					}
				}
			}

			return errors;
		}

		protected List<ValidationError> combine(List<ValidationError> l1, List<ValidationError> l2)
		{
			if (l1 == null && l2 != null)
				return l2;
			else if (l1 != null && l2 == null)
				return l1;
			else if (l1 == null && l2 == null)
				return null;
			l1.AddRange(l2);
			return l1;
		}

		public void setFieldValidated(string name, bool state)
		{
			IBizObjField fld = type.getField(name, true);
			if (fld != null)
				ValidatedFlags.setBit(fld.BizObjIndex, state);
		}

		/// <summary>
		/// If any field in this bizobj is dirty then this will return true, else false.
		/// </summary>
		public bool isDirty()
		{
			return DirtyFlags.AnySet;
		}

		/// <summary>
		/// If the given field is dirty returns true, else false.  This method supports deep names.
		/// </summary>
		/// <seealso cref="IBizObj.isDirty"/>
		/// <param name="name">The field name to check is dirty.</param>
		public bool isDirty(string name)
		{
			bool isIt = false;
            if (NameAddress.isSingleAddress(name))
            {
                BizObjField field = m_bizObjType.getField(name, true) as BizObjField;
                if (field != null)
                    isIt = DirtyFlags.getBit(field.BizObjIndex);
            }
            else
            {
                object first = get(NameAddress.getFirstAddress(name));
                if (first is IBizObj)
                    isIt = ((IBizObj)first).isDirty(NameAddress.getRemainingAddress(name));
                else
                {
                    BizObjField field = m_bizObjType.getField(NameAddress.getFirstAddress(name), true) as BizObjField;
                    if (field != null)
                        isIt = DirtyFlags.getBit(field.BizObjIndex);
                }
            }

			return isIt;
		}

		/// <summary>
		/// Resets all the dirty flags set so that no field is marked as being dirty.
		/// </summary>
		public void clearDirtyFlags()
		{
			DirtyFlags.Clear();
			DirtyFlags.DefaultValue = false;
			foreach (BizObjField fld in type.FieldList)
				if (fld.BizType == BizFieldType.BizObj)
					((IBizObj)get(fld.Name)).clearDirtyFlags();
		}
		public void setAllFieldsDirty()
		{
			DirtyFlags.Clear();
			DirtyFlags.DefaultValue = true;
		}
		public void setDirtyFlag(string fieldName, bool val)
		{
			BizObjField field = m_bizObjType.Fields[fieldName] as BizObjField;
			if(field != null)
				DirtyFlags.setBit(field.BizObjIndex, val);
		}

		/// <summary>
		/// Checks to see if the given value is valid for the field named.  This method
		/// supports deep names as long as the final element in the deep name support is
		/// also an IBizObj.
		/// </summary>
		/// <param name="name">The name of the field to validate the new value against.</param>
		/// <param name="newValue">The value to validate</param>
		/// <returns>If a good value, returns null, otherwise it returns an exception
		/// object that represents the reason why the value is not valid.</returns>
		public BadBizObjFieldException validateField(string name, object newValue)
		{
			if (NameAddress.isSingleAddress(name))
			{
				BizObjField field = m_bizObjType.Fields[name] as BizObjField;
				if (field != null)
				{
					object[] msgAndValue = validateAndConvert(field, newValue);
					if (msgAndValue[0] != null)
						return new BadBizObjFieldException(this, name, (string)msgAndValue[0]);
					return null;
				}
			}
			else
			{
				IBizObj bizobj = get(NameAddress.getAllButLast(name)) as IBizObj;
				if (bizobj != null)
					return bizobj.validateField(NameAddress.getLastAddress(name), newValue);
			}

			return new BadBizObjFieldException(this, name, "no such field");
		}

		///<see>IBizObj.swap</see>
		public void swap(IBizObj that)
		{
			//TODO: Implement this method.
		}

		/// <summary>
		/// Will return XML that represents this bizobj, and will drill down into child bizobj and bizobjlists to complete
		/// the operation.
		/// </summary>
		public string getXml(string name, bool setOnly, bool dirtyOnly, bool newOnly, bool getBasic, bool getObjs, bool getILists, bool getSysVals, XmlTextWriter xmlOut)
		{
			return getXml(name, BaseBizObj.DefaultFieldDecoInfo, setOnly, dirtyOnly, newOnly, getBasic, getObjs, getILists, getSysVals, xmlOut);
		}
		public string getXml(string name, string decoInfo, bool setOnly, bool dirtyOnly, bool newOnly, bool getBasic, bool getObjs, bool getILists, bool getSysVals, XmlTextWriter xmlOut)
		{
			StringWriter strBuf = null;
			bool madeXmlOut = false;
			if (xmlOut == null)
			{
				strBuf = new StringWriter();
				xmlOut = new XmlTextWriter(strBuf);
				madeXmlOut = true;
				xmlOut.Formatting = Formatting.Indented;
			}
			bool needToWriteElements = !BizObjManager.SuggestAttributesForXML;
			bool hasDefaultToWrite = false;
			if (name == null || name.Equals(""))
				name = m_bizObjType.Name;

			//Start with the basic XML element
			xmlOut.WriteStartElement(name);

			//Loop through the fields and process all items that should be attributes
			string fldVal = null;
			object fldObj = null;
			foreach (IBizObjField field in m_bizObjType.OrderedFields)
			{
				IDecorator d = (IDecorator)field.Decorators["xmlOut"];
				BasicFieldWriteOption writeOpt = calcFieldWriteOption(field, d, dirtyOnly, setOnly);
				if (writeOpt == BasicFieldWriteOption.element)
				{
					if (field.Name.Length != 0)
						needToWriteElements = true;
					else
						hasDefaultToWrite = true;
				}
				else if (writeOpt == BasicFieldWriteOption.attrib)
				{
					fldObj = get(field.Name,decoInfo);
					if (fldObj != null)
						fldVal = fldObj.ToString();
					else
						fldVal = BizObjManager.NullValueIndicatorName;

					if (field.Name.Length != 0)
						xmlOut.WriteAttributeString(field.Name, fldVal);
					else
						hasDefaultToWrite = true;
				}
			}

			//Write out sys properties if requested
			if (getSysVals && BizObjManager.SuggestAttributesForXML)
			{
				xmlOut.WriteAttributeString(BaseBizObj.DirtyFlagName, DirtyFlags.getArrayInfo());
				xmlOut.WriteAttributeString(BaseBizObj.ValidatedFlagName, ValidatedFlags.getArrayInfo());
				xmlOut.WriteAttributeString(BaseBizObj.IsNewFlagName, isNew.ToString());
				xmlOut.WriteAttributeString(BaseBizObj.IsDeletedFlagName, isDeleted.ToString());
			}

			if (hasDefaultToWrite)
			{
				xmlOut.WriteString(get("").ToString());
			}

			if (needToWriteElements)
			{
				IBizObj b = null;
				IBizObjList cur = null;
				foreach (IBizObjField field in m_bizObjType.OrderedFields)
				{
					if (field.Name.Length == 0)
						continue;
					IDecorator d = (IDecorator)field.Decorators["xmlOut"];
					bool isFieldHidden = (d != null) ? d.Value.Equals("hidden") : false;
                    IBizObjXmlOutFormatter fmtr = null;
					BasicFieldWriteOption writeOpt = calcFieldWriteOption(field, d, dirtyOnly, setOnly, out fmtr);
					if (writeOpt == BasicFieldWriteOption.element)
					{
						if (field.BizType == BizFieldType.BizObj && getObjs)
							try
							{
								object o = get(field.Name, decoInfo);
								if (o is IBizObj)
								{
									b = (IBizObj)get(field.Name);
									if (!dirtyOnly || (dirtyOnly && b.isDirty()))
										b.getXml(field.Name, decoInfo, setOnly, dirtyOnly, newOnly, getBasic, getObjs, getILists, getSysVals, xmlOut);
								}
								else if (o != null)
									xmlOut.WriteElementString(field.Name, o.ToString());
							}
							catch (BadBizObjFieldException)
							{
								xmlOut.WriteElementString("CriticalError", field.Name);
							}
						else if (field.BizType == BizFieldType.BizObjList && getILists)
							try
							{
								object o = get(field.Name, decoInfo);
								if (o is IBizObjList)
								{
									cur = (IBizObjList)get(field.Name);
									if (cur.numElements > 0)
										cur.getXml(isFieldHidden ? "" : field.Name, decoInfo, setOnly, dirtyOnly, newOnly, getBasic, getObjs, getILists, getSysVals, xmlOut);
								}
								else if (o != null)
									xmlOut.WriteElementString(field.Name, o.ToString());
							}
							catch (BadBizObjFieldException)
							{
								xmlOut.WriteElementString("CriticalError", field.Name);
							}
						else
						{
							fldObj = get(field.Name, decoInfo);
							if (fldObj != null)
								fldVal = fldObj.ToString();
							else
								fldVal = BizObjManager.NullValueIndicatorName;

							if (fldObj != null)
							{
								if (d != null && string.Compare(d.Value, "rawElement", true) == 0)
								{
									xmlOut.WriteStartElement(field.Name);
									xmlOut.WriteRaw(fldVal);
									xmlOut.WriteEndElement();
								}
								else if (d != null && string.Compare(d.Value, "cdata", true) == 0)
								{
									xmlOut.WriteStartElement(field.Name);
									xmlOut.WriteCData(fldVal);
									xmlOut.WriteEndElement();
								}
                                else if (d != null && (string.Compare(d.Value, "format", true) == 0) && fmtr != null)
                                {
                                    fmtr.Format(xmlOut, fldVal);
                                }
                                else
                                    xmlOut.WriteElementString(field.Name, fldVal);
							}
							else
							{
								xmlOut.WriteStartElement(field.Name);
								if (!BizObjManager.NullAsElementAttribute)
								{
									xmlOut.WriteStartElement(fldVal);
									xmlOut.WriteEndElement();
								}
								else
									xmlOut.WriteAttributeString(fldVal, "true");
								xmlOut.WriteEndElement();
							}
						}
					}
				}
			}

			if (getSysVals && !BizObjManager.SuggestAttributesForXML)
			{
				xmlOut.WriteElementString(BaseBizObj.DirtyFlagName, DirtyFlags.getArrayInfo());
				xmlOut.WriteElementString(BaseBizObj.ValidatedFlagName, ValidatedFlags.getArrayInfo());
				xmlOut.WriteElementString(BaseBizObj.IsNewFlagName, isNew.ToString());
				xmlOut.WriteElementString(BaseBizObj.IsDeletedFlagName, isDeleted.ToString());
			}

			xmlOut.WriteEndElement();
			if (madeXmlOut)
			{
				xmlOut.Close();
				strBuf.Close();
				return strBuf.ToString();
			}
			return null;
		}

		/// <summary>
		/// Will return the value of the key for this bizobj.  Most bizobj definitions
		/// will have one or more fields used as keys which indicate uniqueness amongst a 
		/// collection of like bizobjs.  If the key is a single field the value returned
		/// will the be native type of the key.  If the key is a collection of fields, each
		/// field's value will be turned to a string (with ToString method) and concatonated.
		/// </summary>
		public object KeyValue
		{
			get
			{
				if (m_bizObjType.KeyFields.Count == 0)
					return null;
				if (m_bizObjType.KeyFields.Count == 1)
					return get(((BizObjField)m_bizObjType.KeyFields[0]).Name);
				else
				{
					CompositeComparable comparable = new CompositeComparable();
					//StringBuilder buff = new StringBuilder(100);

					foreach (BizObjField field in m_bizObjType.KeyFields)
					{
						object v = get(field.Name);

						comparable.Add(v);
					//	if(v != null)
					//		buff.Append(v.ToString());
					}
					//return buff.ToString();
					return comparable;
				}
			}
		}

		public virtual IBizObj Reference
		{
			get { return this; }
		}

		public IDecorator getFieldDecoration(string fieldName, string decoratorName)
		{
			BizObjField field = m_bizObjType.getField(fieldName, true) as BizObjField;
			IDecorator d = null;
			if (field != null)
				d = field.Decorators[decoratorName] as IDecorator;
			return d;
		}

		public bool containsField(string name)
		{
			if (name == null)
				return false;

            if (NameAddress.isSingleAddress(name))
                return m_bizObjType.Fields.Contains(name);
            else
            {
                string firstName = NameAddress.getFirstAddress(name);
                if (m_bizObjType.Fields.Contains(firstName))
                {
                    INameAddressSupport first = get(firstName + ".") as INameAddressSupport;
                    return (first != null) ? first.containsField(NameAddress.getRemainingAddress(name)) : false;
                }
                return false;
            }
		}
		#endregion

		#region INameAddressSupport Members


		public void put(string nameAddress, object val, object extraInfo)
		{
			string firstName = NameAddress.getFirstAddress(nameAddress);
			if (firstName != null)
			{
				BizObjField field = m_bizObjType.Fields[firstName] as BizObjField;
				if(field != null)
				{
					if (NameAddress.isSingleAddress(nameAddress))
					{
						bool shouldValueBeFieldType = true;
						if (field.HasStandInDecorators)
							foreach (IDecorator deco in field.Decorators.Values)
								if (deco.IsStandin && deco is IBizObjDecorator)
									((IBizObjDecorator)deco).put(this, field, ref val, extraInfo, ref shouldValueBeFieldType);
						putSingleName(firstName, val, extraInfo, shouldValueBeFieldType);
						DirtyFlags.setBit(field.BizObjIndex, true);
						ValidatedFlags.setBit(field.BizObjIndex, false);
					}
					else
					{
						//By calling get here, any decorators on our field will be called before we get the
						//final value.
						INameAddressSupport fieldVal = get(firstName) as INameAddressSupport;
						if (fieldVal != null)
							fieldVal.put(NameAddress.getRemainingAddress(nameAddress), val, extraInfo);

						//If the field has standin decorators we actually have to put the fieldVal back into itself
						if (field.HasStandInDecorators)
						{
							bool shouldValueBeFieldType = true;
							foreach (IDecorator deco in field.Decorators.Values)
								if (deco.IsStandin && deco is IBizObjDecorator)
									((IBizObjDecorator)deco).put(this, field, ref val, null, ref shouldValueBeFieldType);
							putSingleName(firstName, val, extraInfo, shouldValueBeFieldType);
						}
					}
				}
				else
					throw new BadBizObjFieldException(m_bizObjType.Name, firstName, "Bad field name");
			}
		}

		public object get(string nameAddress, object oneArg)
		{
			return get(nameAddress, new object[] { oneArg });
		}

		public object get(string nameAddress)
		{
			if (string.Compare(nameAddress, "_keyValue", false) == 0)
				return KeyValue;

			object val = null;
			string firstName = NameAddress.getFirstAddress(nameAddress);
			if(!Cmn.IsEmpty(firstName))
			{
				BizObjField field = m_bizObjType.Fields[firstName] as BizObjField;
				if (field != null)
				{
					val = getSingleName(firstName);
					if (field.HasStandInDecorators)
					{
						bool shouldValueBeFieldType = true;
						foreach (IDecorator deco in field.Decorators.Values)
							if (deco.IsStandin && deco is IBizObjDecorator)
								((IBizObjDecorator)deco).get(this, field, ref val, null, ref shouldValueBeFieldType);

						if (shouldValueBeFieldType)
							val = createPlatformTypeInstance(field, val);
					}

					if (val is INameAddressSupport)
						val = ((INameAddressSupport)val).get(NameAddress.getRemainingAddress(nameAddress));
				}
				else
					throw new BadBizObjFieldException(m_bizObjType.Name,firstName,"Bad field name");
			}
			else
				val = this;
			
			return val;
		}

		public bool isGetOnly
		{
			get { return false; }
		}

		public XmlSerializationSupport SerializationSupport
		{
			get { return XmlSerializationSupport.AsElement; }
		}

		public string Xml
		{
			get { return getXml("public"); }
		}

		public string getXml(string options)
		{
			Regex regex = new Regex(@"name\((\w+)\)");
			string name = "";
			Match m = regex.Match(options);
			if (m.Success)
				name = m.Value;

            regex = new Regex(@"deco\((\w+)\)");
			string decoInfo = BaseBizObj.DefaultFieldDecoInfo;
			m = regex.Match(options);
			if (m.Success)
				decoInfo = m.Groups[1].Value;

			bool setOnly = false;
			bool dirtyOnly = false;
			bool newOnly = false;
			bool getBasic = false;
			bool getObjs = false;
			bool getILists = false;
			bool getSysVals = false;

			if (options.IndexOf("public") == 0)
			{
				setOnly = BizObjManager.XmlOutSetOnly;
				dirtyOnly = false;
				newOnly = false;
				getBasic = true;
				getObjs = true;
				getILists = true;
				getSysVals = false;
			}
			else if (options.IndexOf("all") == 0)
			{
				setOnly = false;
				dirtyOnly = false;
				newOnly = false;
				getBasic = true;
				getObjs = true;
				getILists = true;
				getSysVals = true;
			}
			if (options.IndexOf("setOnly") != -1) setOnly = true;
			if (options.IndexOf("dirtyOnly") != -1) dirtyOnly = true;
			if (options.IndexOf("newOnly") != -1) newOnly = true;
			if (options.IndexOf("basicProperties") != -1) getBasic = true;
			if (options.IndexOf("objProperties") != -1) getObjs = true;
			if (options.IndexOf("listProperties") != -1) getILists = true;
			if (options.IndexOf("sysProperties") != -1) getSysVals = true;

			return getXml(name, decoInfo, setOnly, dirtyOnly, newOnly, getBasic, getObjs, getILists, getSysVals, null);
		}

		#endregion

		/// <summary>
		/// Validates that <c>value</c> is appropriate for field number <c>index</c>.
		/// If it is valid, converts the <c>value</c> as needed and returns the converted value.
		/// If it is invalid, returns a human-readable error message <c>String</c> describing what went wrong.
		/// See <c>put()</c> for exactly what is and what is not considered "valid."
		/// </summary>
		/// <param name="index">the index of the field that is being validated/converted.
		/// E.g., <c>this.info_.fields[index]</c> is the info about the field.
		/// </param>
		/// <param name="newValue">the value to be validated.</param>
		/// <returns>
		/// A two-element array: the first element is the human-readable error message
		/// describing what went wrong (or <c>null</c> on success), and the second
		/// element is the (possibly) converted <c>value</c>.
		/// </returns>
		protected internal object[] validateAndConvert(BizObjField field, object newValue)
		{
			StringBuilder buff = new StringBuilder();
			if (field.BizType == BizFieldType.NetClass)
			{
				System.Type target = null;
				if (field.SampleValue != null)
					target = field.SampleValue.GetType();

				if (field.Validators != null)
				{
					string strNewValue = newValue == null ? null : newValue.ToString();
					for (int p = 0; p < field.Validators.Count; p++)
					{
						FieldValidator v = (FieldValidator)field.Validators[p];
						string s = v.validateNoThrow(this, field.Name, strNewValue);
						if (s != null)
						{
							buff.Append(s);
							if (p < field.Validators.Count - 1)
								buff.Append(";");
						}
					}
				}
				if (buff.Length == 0)
					try
					{
						newValue = createPlatformTypeInstance(field, newValue);
						if (newValue is INameAddressSupport)
							newValue = ((INameAddressSupport)newValue).get(null);
					}
					catch (Exception)
					{
						buff.AppendFormat("Not valid format for field;", field.Name);
					}
			}
			else if (field.BizType == BizFieldType.BizObj && newValue is IBizObj &&
				field.SampleValue != null)
			{
				if (((IBizObj)field.SampleValue).type.Name.CompareTo(((IBizObj)newValue).type.Name) != 0)
				{
					// if they're 'BusinessObject's and the 'type()'s don't match, it's an error
					buff.Append(String.Format("Type mismatch: expected {0}, found {1}",
						((IBizObj)field.SampleValue).type.Name, ((IBizObj)newValue).type.Name));
				}
			}

			return new object[] { (buff.Length == 0)?null:buff.ToString(), newValue };
		}

		protected enum BasicFieldWriteOption { none, attrib, element };
        protected BasicFieldWriteOption calcFieldWriteOption(IBizObjField fld, IDecorator d, bool dirtyOnly, bool setOnly)
        {
            IBizObjXmlOutFormatter fmtr = null;
            return calcFieldWriteOption(fld, d, dirtyOnly, setOnly, out fmtr);
        }
        protected BasicFieldWriteOption calcFieldWriteOption(IBizObjField fld, IDecorator d, bool dirtyOnly, bool setOnly, out IBizObjXmlOutFormatter optionalFormatter)
		{
            optionalFormatter = null;
            if (d != null && String.Compare("format", d.Value) == 0)
            {
                IDecorator dec = fld.Decorators["xmlOutFmt"] as IDecorator;
                IClassFactory xmlOutFmtFactory = Application.currentApp.factories["BizObjXmlOutFormatters"];
                if (xmlOutFmtFactory != null)
                {
                    // try and create the optional formatter
                    optionalFormatter = xmlOutFmtFactory.createFromNickName(dec.Value, fld) as IBizObjXmlOutFormatter;
                    if (optionalFormatter != null)
                    {
                        // If formatter type is Default then let the code below determine the type of Write Option
                        if (optionalFormatter.FormatterType != BizObjXmlOutFormatterType.Default)
                        {
                            // Specified Formatter type so use it
                            return (optionalFormatter.FormatterType == BizObjXmlOutFormatterType.Attribute) ? BasicFieldWriteOption.attrib : BasicFieldWriteOption.element;
                        }
                    }
                }
            }

            BasicFieldWriteOption rc = BasicFieldWriteOption.none;

            if (d != null && string.Compare("none", d.Value) == 0)
			{
				return rc;
			}
			else if ((d != null && string.Compare("always", d.Value) == 0) || (fld.BizType != BizFieldType.Method ||
				(fld.BizType == BizFieldType.Method && d != null && string.Compare("none", d.Value) != 0)) &&
				(!dirtyOnly || (dirtyOnly && this.isDirty(fld.Name))) &&
				(!setOnly || (setOnly && this.isFieldSet(fld.Name))))
			{
				if (!(BizObjManager.SuggestAttributesForXML && d == null ||
						(d != null && d.Value.StartsWith("attrib"))) ||
						fld.BizType != BizFieldType.NetClass)
					rc = BasicFieldWriteOption.element;
				else if (fld.BizType == BizFieldType.NetClass)
					rc = BasicFieldWriteOption.attrib;
			}
			return rc;
		}


		/// <summary>
		/// This method will attempt to put the value into the field by doing decoration first and then converting the
		/// results into the expected field type.
		/// </summary>
		/// <param name="fld"></param>
		/// <param name="val"></param>
		protected void putSingleNameProcessed(BizObjField field, object val, string extraInfo)
		{
			if (BizObjManager.NullValueIndicatorName.CompareTo(val) == 0)
				val = null;
			bool shouldValueBeFieldType = true;
			if (field.HasStandInDecorators)
				foreach (IDecorator deco in field.Decorators.Values)
					if (deco.IsStandin && deco is IBizObjDecorator)
						((IBizObjDecorator)deco).put(this, field, ref val, extraInfo, ref shouldValueBeFieldType);
			if (val != null &&(shouldValueBeFieldType && val.GetType() != field.PlatformType))
				val = createPlatformTypeInstance(field, val);
			putSingleName(field.Name, val, extraInfo, shouldValueBeFieldType);
		}

		#region ICustomTypeDescriptor Members

#if(!COMPACT_FRAMEWORK)
		AttributeCollection ICustomTypeDescriptor.GetAttributes()
		{
			return TypeDescriptor.GetAttributes(this, true);
		}

		string ICustomTypeDescriptor.GetClassName()
		{
			return TypeDescriptor.GetClassName(this, true);
		}

		string ICustomTypeDescriptor.GetComponentName()
		{
			return TypeDescriptor.GetComponentName(this, true);
		}

		TypeConverter ICustomTypeDescriptor.GetConverter()
		{
			return TypeDescriptor.GetConverter(this, true);
		}

		EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
		{
			return TypeDescriptor.GetDefaultEvent(this, true);
		}

		PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
		{
			return null;
		}

		object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
		{
			return TypeDescriptor.GetEditor(this, editorBaseType, true);
		}
#else
		AttributeCollection ICustomTypeDescriptor.GetAttributes() {return null;}
		string ICustomTypeDescriptor.GetClassName() {return null;}
		string ICustomTypeDescriptor.GetComponentName() {return null;}
		TypeConverter ICustomTypeDescriptor.GetConverter() {return null;}
		EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() {return null;}
		PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() {return null;}
		object ICustomTypeDescriptor.GetEditor(Type editorBaseType) {return null;}
#endif
		EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
		{
			return TypeDescriptor.GetEvents(this, true);
		}

		EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
		{
			return TypeDescriptor.GetEvents(this, attributes, true);
		}

		PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
		{
			return ((ICustomTypeDescriptor)this).GetProperties(new Attribute[0]);
		}

		PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
		{

			//return TypeDescriptor.GetProperties(this, attributes, true);
			PropertyDescriptor[] propArray = new PropertyDescriptor[m_bizObjType.Fields.Count];
			int pos = 0;
			foreach (BizObjField field in m_bizObjType.Fields.Values)
			{
				propArray[pos++] = new BizObjPropertyDescriptor(field, this);
			}
			return new PropertyDescriptorCollection(propArray);

		}

		object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
		{
			return this;
		}

		#endregion
	}
}
